/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2015.07.10                                          *
 * Description:      all regions info                                    *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/info/RegionInfo.h"

//COMMON headers
#include "COMMON/info/InfoManager.h"
#include "COMMON/regn/Region.h"
#include "COMMON/regn/SimpleRegion.h"
#include "COMMON/tbox/LogMessage.h"
#include "COMMON/tbox/Utilities.h"



namespace FreeMAPs
{

RegionInfo::RegionInfo(
    const std::string& object_name):
    Parent ("base_information", object_name, false)
{}



RegionInfo::~RegionInfo()
{
    this->clear();
}



void
RegionInfo::clear()
{
    Utilities::clear_pointer_object (d_regions);
    d_simple_regions.clear();
}



Region*
RegionInfo::get_region (
    const std::string& region_name)
{
    std::map<std::string, Region*>::const_iterator find = d_regions.find (region_name);
    FREEMAPS_ASSERT (find != d_regions.end());
    find->second->check_state();

    return find->second;
}



const SimpleRegion*
RegionInfo::get_simple_region (
    const std::string& simple_region_name) const
{
    std::map<std::string, const SimpleRegion*>::const_iterator find = d_simple_regions.find (simple_region_name);
    //std::cout << "simple_region_name = \"" << simple_region_name << "\"" << std::endl;
    //if (find == d_simple_regions.end())
    //{
        //int ii = 0;
        //++ii;
    //}
    FREEMAPS_ASSERT (find != d_simple_regions.end());
    //find->second->check_state();

    return find->second;
}



bool
RegionInfo::check_all_info_db (
        const InputDatabase* db)
{
    if( !db->key_exists("region_names") )
    {
        FREEMAPS_ERROR("in \"region_info\", can not find  \"region_names\" \n");
        return false;
    }
    if( !db->is_string("region_names") )
    {
        FREEMAPS_ERROR("in \"region_info\", \"region_names\" should be string \n");
        return false;
    }

    const std::vector<std::string> region_names = db->get_string_vector ("region_names");
    for (std::size_t i = 0; i < region_names.size(); i++)
    {
        const std::string& region_name = region_names[i];

        if( db->key_exists(region_name) )
        {
            if( !db->is_database(region_name) )
            {
                FREEMAPS_ERROR("in \"region_info\",  \"" + region_name + "\", should be database, like this--> A \n{\n ......\n} \n");
                return false;
            }

            const InputDatabase* db_region = db->get_database(region_name);
            Region* region = new Region (this->get_full_name(), region_name);
            bool check_ok = region->check_all_info_db(db_region);
            if( !check_ok )
            {
                return false;
            }
        }
        else
        {
            FREEMAPS_ERROR("in \"region_info\", can not find database \"" + region_name + "\" , but in \"region_names\" exist\n");
            return false;
        }
    }

    return true;
}



void
RegionInfo::get_from_input (
        const InputDatabase* db)
{
    this->prepare_for_use (db);

    db->insert_allowable_key_with_items ("region_names");
    db->check_allowable_keys();

    /*
     * build region_names
     */
    {
        db->check_optional_key ("region_names", DataType::STRING, false, true);
        db->check_duplicated_string_value ("region_names");
        if (db->key_exists ("region_names") && db->is_string("region_names"))
        {
            const std::vector<std::string> region_names = db->get_string_vector ("region_names");
            for (std::size_t i = 0; i < region_names.size(); ++i)
            {
                const std::string& region_name = region_names[i];

                // exist && is database
                if ( db->key_exists (region_name) && db->is_database(region_name) )
                {
                    Region* region = new Region (this->get_full_name(), region_name);

                    db->check_requisite_key (region_name, DataType::DATABASE, false, false);
                    region->get_from_input (db->get_database (region_name));
                    d_regions.insert (std::make_pair (region_name, region));

                    this->insert_simple_regions (db, region);
                }
            }

            this->add_key_for_checking_consistence (
                "region_names",
                "check that two regions can NOT own same attribute");
        }
    }
}



void
RegionInfo::insert_simple_regions (
    const InputDatabase* db,
    const Region* region)
{
    const std::map<std::string, SimpleRegion*>& simple_regions = region->get_simple_regions();
    for (std::map<std::string, SimpleRegion*>::const_iterator pos = simple_regions.begin();
            pos != simple_regions.end(); ++pos)
    {
        const std::string& simple_region_name = pos->first;
        if (d_simple_regions.count (simple_region_name) > 0)
        {
            LogMessage::unknown_error (
                db,
                "two region have same simple regions name " + simple_region_name);
        }

        d_simple_regions.insert (std::make_pair (pos->first, pos->second));
    }
}



std::ostream&
operator << (
    std::ostream& os,
    const RegionInfo& rhs)
{
    os << "region_info" << RTN_FLAG << "{" << RTN_FLAG;

    if (!rhs.d_regions.empty())
    {
        os << LogMessage::get_log_info_from_map_keys (
               "region_names",
               rhs.d_regions,
               ONE_TAB,
               true)  << RTN_FLAG;

        for (std::map<std::string, Region*>::const_iterator pos = rhs.d_regions.begin();
                pos != rhs.d_regions.end(); ++pos)
        {
            std::ostringstream oss;
            oss << *pos->second;
            os  << RTN_FLAG << LogMessage::get_log_info (oss, ONE_TAB)  << RTN_FLAG;
        }
    }
    os << "}";

    return os;
}



void
RegionInfo::add_region(
        const std::string region_name,
        const std::map<std::string, std::string> map_simple_to_attr_name)
{
    Region* region = new Region (this->get_full_name(), region_name);//band

    std::map<std::string, std::string>::const_iterator iter = map_simple_to_attr_name.begin();
    for( ; iter != map_simple_to_attr_name.end(); iter++)
    {
        region->add_simple_region(iter->first, iter->second);
    }
    d_regions.insert(std::make_pair(region_name, region));
}

} //namespace FreeMAps
